﻿namespace SimpleWebServices.Configuration
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using Castle.MicroKernel;
    using Castle.Windsor;
    using Castle.Windsor.Configuration.Interpreters;

    public static class ServiceLocator
    {
        public const string ConfigFileName = "windsor.config";
        private static readonly object _lock = new object();
        private static IWindsorContainer _container;

        public static bool Initialized
        {
            get;
            private set;
        }

        public static string GetConfigFilePath(string fileName)
        {
            if (null == fileName)
            {
                throw new ArgumentNullException("fileName");
            }
            else if (0 == fileName.Length)
            {
                throw new ArgumentOutOfRangeException("fileName");
            }

            string applicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            string filePath = Path.Combine(applicationBase, fileName);

            if (!File.Exists(filePath))
            {
                // TODO : ask James if this is cool : applicationBase += @"\bin";
                filePath = Path.Combine(applicationBase, fileName);
            }

            return filePath;
        }

        public static void Reset()
        {
            if (null != _container)
            {
                _container.Dispose();
                _container = null;
                ServiceLocator.Initialized = false;
            }
        }

        public static void Clear()
        {
            _container = new WindsorContainer();
            ServiceLocator.Initialized = true;
        }

        public static TInterface Resolve<TInterface>()
        {
            if (!typeof(TInterface).IsInterface)
            {
                throw new ArgumentInvalidGenericException("TInterface");
            }

            ServiceLocator.EnsureInitialized();
            try
            {
                return _container.Resolve<TInterface>();
            }
            catch (ComponentNotFoundException exception)
            {
                throw new KeyNotFoundException(exception.Message, exception);
            }
        }

        public static TInterface Resolve<TInterface>(string key)
        {
            if (null == key)
            {
                throw new ArgumentNullException("key");
            }
            else if (0 == key.Length)
            {
                throw new ArgumentOutOfRangeException("key");
            }
            else if (!typeof(TInterface).IsInterface)
            {
                throw new ArgumentInvalidGenericException("TInterface");
            }

            ServiceLocator.EnsureInitialized();
            try
            {
                return _container.Resolve<TInterface>(key);
            }
            catch (ComponentNotFoundException exception)
            {
                throw new KeyNotFoundException(exception.Message, exception);
            }
        }

        public static void Register<TInterface, TConcrete>()
        {
            string key = string.Format(CultureInfo.InvariantCulture, "{0}, {1}", typeof(TInterface).FullName, typeof(TConcrete).FullName);

            ServiceLocator.Register<TInterface, TConcrete>(key);
        }

        public static void Register<TInterface, TConcrete>(string key)
        {
            if (!typeof(TInterface).IsInterface)
            {
                throw new ArgumentInvalidGenericException("TInterface.");
            }
            else if (typeof(TConcrete).IsInterface || typeof(TConcrete).IsAbstract)
            {
                throw new ArgumentInvalidGenericException("TConcrete");
            }
            else if (null == key)
            {
                throw new ArgumentNullException("key");
            }
            else if (key.Length.Equals(0))
            {
                throw new ArgumentOutOfRangeException("key");
            }

            ServiceLocator.EnsureInitialized();
            _container.AddComponent(key, typeof(TInterface), typeof(TConcrete));
        }

        public static void Register<TInterface>(object instance)
        {
            if (!typeof(TInterface).IsInterface)
            {
                throw new ArgumentInvalidGenericException("TInterface.");
            }
            else if (null == instance)
            {
                throw new ArgumentNullException("instance");
            }

            ServiceLocator.EnsureInitialized();
            _container.Kernel.AddComponentInstance<TInterface>(instance);
        }

        public static void Register(string key, object instance)
        {
            if (null == instance)
            {
                throw new ArgumentNullException("instance");
            }
            else if (null == key)
            {
                throw new ArgumentNullException("key");
            }
            else if (0 == key.Length)
            {
                throw new ArgumentOutOfRangeException("key");
            }

            ServiceLocator.EnsureInitialized();
            _container.Kernel.AddComponentInstance(key, instance);
        }
        
        private static void EnsureInitialized()
        {
            if (!ServiceLocator.Initialized)
            {
                lock (_lock)
                {
                    if (!ServiceLocator.Initialized)
                    {
                        _container = new WindsorContainer(new XmlInterpreter(ServiceLocator.GetConfigFilePath(ConfigFileName)));
                        ServiceLocator.Initialized = true;
                    }
                }
            }
        }
    }
}
